/*
	Go 语言中的 gin 框架
	Gin 是一个用 Go (Golang) 编写的 HTTP web 框架。
	它具有类似于 martini 的 API 以及更好的性能，同时也保留了 martini 的一些特性，
	例如：无侵入性的路由匹配、中间件、组等。

	官方文档：https://gin-gonic.com/zh-cn/docs/
	中文文档：https://www.kancloud.cn/shuangdeyu/gin_book/949763

	安装：
		go get -u github.com/gin-gonic/gin

	使用：
		1. 导入包
			import "github.com/gin-gonic/gin"
		2. 创建路由
			router := gin.Default()
		3. 路由分组
			router := gin.Default()
			v1 := router.Group("/v1")
			{
				v1.POST("/login", loginEndpoint)
				v1.POST("/submit", submitEndpoint)
				v1.POST("/read", readEndpoint)
			}
		4. 路由处理
			router := gin.Default()
			router.GET("/someGet", getting)
			router.POST("/somePost", posting)
			router.PUT("/somePut", putting)
			router.DELETE("/someDelete", deleting)
			router.PATCH("/somePatch", patching)
			router.HEAD("/someHead", head)
			router.OPTIONS("/someOptions", options)
		5. 路由参数
			router := gin.Default()
			// 正则表达式
			router.GET("/user/:name/*action", func(c *gin.Context) {
				name := c.Param("name")
				action := c.Param("action")
				message := name + " is " + action
				c.String(http.StatusOK, message)
			})
			// 查询参数
			router.GET("/welcome", func(c *gin.Context) {
				firstname := c.DefaultQuery("firstname", "Guest")
				lastname := c.Query("lastname") // 是 c.Request.URL.Query().Get("lastname") 的简写
				c.String(http.StatusOK, "Hello %s %s", firstname, lastname)
			})
		6. 路由重定向
			router := gin.Default()
			router.GET("/test", func(c *gin.Context) {
				c.Redirect(http.StatusMovedPermanently, "http://www.baidu.com")
			}
		7. 路由处理函数
			router := gin.Default()
			router.GET("/someJSON", func(c *gin.Context) {
				c.JSON(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
			}
			router.GET("/moreJSON", func(c *gin.Context) {
				// 你也可以使用一个结构体
				var msg struct {
					Name    string `json:"user"`
					Message string
					Number  int
				}
				msg.Name = "Lena"
				msg.Message = "hey"
				msg.Number = 123
				// 注意 msg.Name 在 JSON 中变成了 "user"，因为在结构体字段中使用了 `json:"user"` 标签
				c.JSON(http.StatusOK, msg)
			}
			router.GET("/someXML", func(c *gin.Context) {
				c.XML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
			}
			router.GET("/someYAML", func(c *gin.Context) {
				c.YAML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
			}
			router.GET("/someProtoBuf", func(c *gin.Context) {
				reps := []int64{int64(1), int64(2)}
				label := "test"
				// 生成 protobuf 数据
				data := &protoexample.Test{
					Label: &label,
					Reps:  reps,
				}
				// 注意，数据将以 protobuf 格式发送
				c.ProtoBuf(http.StatusOK, data)
			}
		8. 路由中间件
			router := gin.Default()
			router.Use(gin.Logger())
			router.Use(gin.Recovery())
			router.GET("/test", func(c *gin.Context) {
				panic("test")
			}
		9. 路由组中间件
			router := gin.Default()
			// Simple group: v1
			v1 := router.Group("/v1")
			v1.Use(middleware1, middleware2)
			{
				v1.POST("/login", loginEndpoint)
				v1.POST("/submit", submitEndpoint)
				v1.POST("/read", readEndpoint)
				// nested group: v1/admin
				admin := v1.Group("admin")
				admin.Use(adminRequired)
				{
					admin.POST("/users", usersEndpoint)
					admin.POST("/products", productsEndpoint)
				}
			}
*/

package main

import (
	"encoding/json"
	"log"
	"net/http"

	"github.com/gin-gonic/gin"
	"github.com/thinkerou/favicon"
)

// 自定义Go中间件 拦截器
func MyMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		// Do something before request
		// 通过自定义的中间件，设置的值，在后续处理只要调用了这个中间件，就可以获取到这个值
		c.Set("usersession", "orzr3-123456")
		c.Next() // 一定要调用该方法，否则后续的处理不会被执行
		// c.Abort() // 一旦调用了该方法，后续的处理都不会被执行
		// Do something after request
	}
}

func main() {
	// 创建服务
	ginServer := gin.Default()
	// 使用 favicon.ico 文件
	ginServer.Use(favicon.New("./favicon.ico"))

	// 加载静态页面
	ginServer.LoadHTMLGlob("templates/*")
	// 加载资源文件
	ginServer.Static("/static", "./static")

	// 或者 LoadHTMLFiles
	// ginServer.LoadHTMLFiles("templates/index.html")
	// 访问地址，处理请求
	ginServer.GET("/hello", func(context *gin.Context) {
		context.JSON(200, gin.H{"message": "hello world"})
	})
	// Gin RESTful API
	ginServer.POST("/user", func(context *gin.Context) {
		context.JSON(200, gin.H{"message": "post user"})
	})

	ginServer.PUT("/user", func(context *gin.Context) {
		context.JSON(200, gin.H{"message": "put user"})
	})

	ginServer.DELETE("/user", func(context *gin.Context) {
		context.JSON(200, gin.H{"message": "delete user"})
	})

	// 响应一个前端页面
	ginServer.GET("/index", func(context *gin.Context) {
		context.HTML(http.StatusOK, "index.html", gin.H{"title": "gin web页面"})
	})

	// 接收前端传递过来的参数
	ginServer.GET("/user/info", func(context *gin.Context) {
		userid := context.Query("userid")
		username := context.Query("username")
		context.JSON(http.StatusOK, gin.H{
			"userid":   userid,
			"username": username,
		})
	})

	// 接收前端传递过来的参数，并且使用中间件
	ginServer.GET("/user/info/handler", MyMiddleware(), func(context *gin.Context) {
		// 获取中间件中设置的值
		userSession := context.MustGet("usersession").(string)
		// 打印
		log.Println("userSession:================>", userSession)

		userid := context.Query("userid")
		username := context.Query("username")
		context.JSON(http.StatusOK, gin.H{
			"userid":      userid,
			"username":    username,
			"userSession": userSession,
		})
	})

	// userinfo/info/123/orzr3
	ginServer.GET("/user/info/:userid/:username", func(context *gin.Context) {
		userid := context.Param("userid")
		username := context.Param("username")
		context.JSON(http.StatusOK, gin.H{
			"userid":   userid,
			"username": username,
		})
	})

	// 前端给后端传递 json 数据
	ginServer.POST("/json", func(context *gin.Context) {
		// request.body
		data, _ := context.GetRawData()
		var m map[string]interface{}
		// 包装成json 数据
		_ = json.Unmarshal(data, &m)
		context.JSON(http.StatusOK, m)
	})

	// user/add 添加数据
	// 支持函数式编程 java不能把函数作为参数传递，go可以
	ginServer.POST("/user/add", func(context *gin.Context) {
		// 获取前端传递过来的参数
		username := context.PostForm("username")
		password := context.PostForm("password")
		if username == "" || password == "" {
			context.JSON(http.StatusOK, gin.H{
				"code": 400,
				"msg":  "参数错误",
			})
			return
		}
		// 返回给前端
		context.JSON(http.StatusOK, gin.H{
			"msg":      "ok",
			"username": username,
			"password": password,
		})
	})

	// 路由重定向
	ginServer.GET("/redirect", func(context *gin.Context) {
		// 重定向 301	http.StatusMovedPermanently
		context.Redirect(http.StatusMovedPermanently, "https://www.bilibili.com")
	})

	// 404 NoRoute
	ginServer.NoRoute(func(context *gin.Context) {
		// context.JSON(http.StatusNotFound, gin.H{
		// 	"code": 404,
		// 	"msg":  "404 not found",
		// })
		context.HTML(http.StatusNotFound, "404.html", nil)
	})

	// 路由组 /system/add 路由组的写法
	systemGroup := ginServer.Group("/system")
	{
		systemGroup.GET("/add", func(context *gin.Context) {
			context.JSON(http.StatusOK, gin.H{
				"msg": "system add",
			})

		})
		systemGroup.DELETE("/delete", func(context *gin.Context) {
			context.JSON(http.StatusOK, gin.H{
				"msg": "system delete",
			})
		})
	}

	// 服务器端口
	ginServer.Run(":8082")
}

/*
 tips:
 为什么需要一个新的语言
 需要一种高效的执行速度，编译速度和开发速度的编程语言

 Docker 就是使用Go进行开发的

 Go编译的程序，可以媲美 C / C++ 的速度，而且更加安全
 它是21世纪的C语言
*/
